<?php
require_once FRAME_ROOT_PATH . '/View.php';
require_once FRAME_ROOT_PATH . '/Exception/RequestException.php';


/**
 * 请求对象
 * 路由->请求对象子类->init-> 具体action->render
 * 目前只有http请求对象,命令行模式与cgi模式暂时不支持
 *
 * @author RD
 */
class Request {
    private $_route  = NULL;
    protected $view  = NULL;
    public $isRewrite = false;

    private $_config = array ('viewPath' => '',
                              'render'   => true,
                              'suffix'   => '.php',
                              'baseUrl'  => '',
                              'host'     => '',
    );

    private $_personalSets = array ();

    /**
     * 初始化
     * @param <Route> $route 路由对象
     */
    public function __construct (Route $route) {
        $this->_route                       = $route;
        $this->isRewrite                    = $route->getisRewrite();
        $this->_config ['viewPath']         = dirname ($this->_route->getControllerPath ()) . '/views';
        $this->_config ['baseUrl']          = $route->getBaseUrl ();
        $this->_config ['host']             = $route->getHost ();
        $this->_config ['moduleKey']        = $route->getModuleKey ();
        $this->_config ['controllerKey']    = $route->getControllerKey ();
        $this->_config ['actionKey']        = $route->getActionKey ();
        $this->_config ['customKey']        = $route->getCustomKey ();
        foreach ($route->getPersonalSets() as $r => $v) {
            $this->_personalSets [$v['module'] . $v['controller'] . $v['action']] = $r;
        }

        $this->view = new View ($this);
    }


    /**
     * 设置视图路径
     *
     * @param <String> $path 视图路径
     * @return this
     */
    final public function setViewPath ( $path ) {
        $this->_config ['viewPath'] = $path;
        return $this;
    }


    /**
     * 设置视图默认后缀名称
     *
     * @param $suffix 后缀名称，如：.php
     * @return this
     */
    final public function setSuffix ( $suffix ) {
        $this->_config ['suffix'] = $suffix;
        return $this;
    }


    /**
     * 设置是否自动渲染
     */
    final public function setAutoRender ( $v ) {
        $this->_config ['render'] = $v;
        return $this;
    }

    /**
     * 获取视图路径
     *
     * @return 视图路径
     */
    final public function getViewPath () {
        return $this->_config ['viewPath'];
    }


    /**
     * 获取主机名称
     */
    final public function getHost () {
        return $this->_config ['host'];
    }


    /**
     * 获取相对路径
     */
    final public function getBaseUrl () {
        return $this->_config ['baseUrl'];
    }

    /**
     * 获取参数 (GET POST)
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    final public function getParam ($key, $defaultValue = NULL) {
        return $this->_route->getParam ($key, $defaultValue);
    }


    /**
     * 获取全部参数
     *
     * @return array
     */
    final public function getAllParams ( ) {
        return $this->_route->getAllParams ();
    }


    /**
     * 获取POST参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    final public function getPost ($key = NULL, $defaultValue = NULL) {
        return $this->_route->getPost ($key, $defaultValue);
    }


    /**
     * 获取SERVER参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getServer ($key = NULL, $defaultValue = NULL) {
        return $this->_route->getServer ($key, $defaultValue);
    }


    /**
     * 获取COOKIE参数
     *
     * @param $key 键名
     * @param $defaultValue 默认值
     * @return mixed value
     */
    public function getCookie ($key = NULL, $defaultValue = NULL) {
        return $this->_route->getCookie ($key, $defaultValue);
    }

    /**
     * 获取客户端ip地址
     * @param <type> $isNum 默认为：false，若为true则返回int格式
     * @return <String|Int>
     */
    public function getIp($isNum = false)
    {
        if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown'))
        $onlineip = getenv('HTTP_CLIENT_IP');
        elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown'))
        $onlineip = getenv('HTTP_X_FORWARDED_FOR');
        elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown'))
        $onlineip = getenv('REMOTE_ADDR');
        elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown'))
        $onlineip = $_SERVER['REMOTE_ADDR'];

        preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches);
        $onlineip = !empty($onlineipmatches[0]) ? $onlineipmatches[0] : 'unknown';
        if($onlineip=='unknown') $onlineip = '0.0.0.0';

        if ($isNum) {
            $onlineip = sprintf ('%u', ip2long ($onlineip));
        }

        return $onlineip;
    }

    /**
     * 渲染视图
     *
     * @param $action 视图名称
     */
    final public function render ( $action ) {
        $this->_config ['render'] = false;
        $this->renderScript ($this->_config['viewPath'] . '/' . $this->getParam ('controller') . '/' . $action . $this->_config ['suffix']);
    }


    /**
     * 渲染视图脚本
     *
     * @param $path 视图脚步绝对路径
     * @return void
     */
    final public function renderScript ( $scriptPath ) {
        $this->_config ['render'] = false;
        if (!file_exists ($scriptPath)) {
            throw new RequestException ('Script Not Found: ' . $scriptPath);
        } else {
            $this->view->run ($scriptPath);
        }
    }


    /**
     * 处理具体action
     */
    final public function doRequest () {
        if ($this->_route == NULL)
        throw new RequestException ('Route Not Found');

        $this->init ();
        $this->{$this->_route->getAction () . 'Action'} ();

        if ($this->_config['render'])
        $this->render ($this->_route->getAction ());
    }


    /**
     * 供覆盖的方法
     */
    public function init () {
    }


    /**
     * 服务器端跳转 使用header命令
     * @param <String> $url 新地址
     */
    protected function _redirect ($url) {
        header ('location:' . $url);
        exit (0);
    }

    /**
     * 输出url地址，所有本程序的地址都有此方法输出
     * @param <String> $baseUrl 相对地址
     * @param <String> $controller 控制器名称
     * @param <String> $action 方法名称
     * @param <String> $param 参数 String:k1=va1&k2=v2 Array: array ('k1'=>v1,'k2'=>v2)
     * @param <String> $module 模块名称 如果为空，则自动使用当前模块名称
     * @return <String> 地址
     */
    public function url ($baseUrl, $controller='index', $action='index', $param='', $gomodule='') {
        if ($gomodule == '') {
            //如果未设置模块名，则自动使用当前模块名称
            $gomodule = $this->getParam( 'module' , "default" );
        }
        
        if ($this->isRewrite) {
            return $this->_RewriteUrl ($baseUrl, $controller, $action, $param, $gomodule);
        } else {
            return $this->_CommonUrl ($baseUrl, $controller, $action, $param, $gomodule);
        }
    }


    /**
     * Rewrite模式下，构造url地址，输出url地址，
     * @param <String> $baseUrl 相对地址
     * @param <String> $controller 控制器名称
     * @param <String> $action 方法名称
     * @param <String> $param 参数 String:k1=va1&k2=v2 Array: array ('k1'=>v1,'k2'=>v2)
     * @param <String> $module 模块名称
     * @return <String> 地址
     */
    private function _RewriteUrl ($baseUrl, $controller, $action, $param, $gomodule)
    {
        if (strrpos ( $baseUrl, '/') == strlen ($baseUrl) -1) {
            $url = substr ($baseUrl, 0, -1);
        } else {
            $url = $baseUrl;
        }
        
        if (isset ( $this->_personalSets [$gomodule . $controller . $action])) {
            $url .= '/' . $this->_personalSets [$gomodule . $controller . $action];
        } else {
            if ($gomodule == 'default') {
                $gomodule = '';
                $url .= '/' . $controller . '/' . $action;
            } else {
                $url .= '/' . $gomodule . '/' . $controller . '/' . $action;
            }
        }
        
        if (is_array ($param)) {
            $extString = '';
            
            foreach ($param as $k => $v) {
                $uev = urlencode ($v);
                
                if (stripos ($uev, '%2f') !== false || stripos ( $uev, '%3c') !== false) {
                    $extString .= empty ($extString)? $k . '=' . $uev : '&' . $k . '=' . $uev;
                } else {
                    $url .= '/' . $k . '/' . urlencode ($v);
                }
            }
            
            if ($extString != '') {
                $url .= '?' . $extString;
            }
        } else {
            if ($param != '') {
                $url .= '?' . $param;
            }
        }
    
        return $url;
    }
    
    
    /**
     * 非Rewrite模式下，构造url地址，输出url地址，
     * @param <String> $baseUrl 相对地址
     * @param <String> $controller 控制器名称
     * @param <String> $action 方法名称
     * @param <String> $param 参数 String:k1=va1&k2=v2 Array: array ('k1'=>v1,'k2'=>v2)
     * @param <String> $module 模块名称
     * @return <String> 地址
     */
    private function _CommonUrl ($baseUrl, $controller, $action, $param, $gomodule)
    {
        if (strrpos ( $baseUrl, '/') == strlen ($baseUrl) -1) {
            $url = $baseUrl;
        } else {
            $url = $baseUrl . '/';
        }
        
        $url .= basename($this->getServer('SCRIPT_NAME', 'index.php'));
        
        if ( isset ($this->_personalSets [$gomodule . $controller . $action])) {
            $url .= '?' . $this->_config ['customKey'] . '=' . $this->_personalSets [$gomodule . $controller . $action];
        } else if ($gomodule == 'default') {
            $url .= '?' .   $this->_config ['controllerKey']    . '=' . $controller
                    . '&' . $this->_config['actionKey']         . '=' . $action;
        } else {
            $url .= '?' .   $this->_config ['moduleKey']        . '=' . $gomodule
                    . '&' . $this->_config ['controllerKey']    . '=' . $controller
                    . '&' . $this->_config ['actionKey']        . '=' . $action;
        }
        
        if ( is_array ($param)) {
            foreach ($param as $k => $v) {
                $url .= '&' . $k . '=' . urlencode ($v);
            }
        } else if ($param != ''){
            $url .= '&' . $param;
        }
        
        return $url;
    }


}